#pragma once

#include <string>
#include <nlohmann/json.hpp>

namespace El {
namespace AITalk {

// Define an enum for activation status outside the class if used externally,
// or inside if only used internally by AITalkOta.
enum class ActivationStatus {
    SUCCESS,
    FAILURE,
    PENDING, // Corresponds to HTTP 202
    NO_CHALLENGE,
    ERROR
};

class AITalkOta {
public:
    // Make constructor private for singleton
    // AITalkOta() = default;
    AITalkOta(const AITalkOta &) = delete;            // Disable copy constructor
    AITalkOta &operator=(const AITalkOta &) = delete; // Disable copy assignment
    ~AITalkOta() = default;

    static AITalkOta &GetInstance();

    /**
     * @brief Starts the OTA and activation process.
     *        Calls Fetch, and if activation is required, calls Activate.
     *
     * @param ota_url The URL of the OTA server.
     * @param device_id The device ID (MAC address) to send in the request.
     * @param client_id The client ID (UUID) to send in the request.
     * @return bool True if Fetch succeeded and activation (if needed) succeeded or is pending, false otherwise.
     */
    bool Start(const std::string &ota_url, const std::string &device_id, const std::string &client_id);

    /**
     * @brief Fetches WebSocket configuration from the OTA server.
     *
     * @param ota_url The URL of the OTA server.
     * @param device_id The device ID (MAC address) to send in the request.
     * @param client_id The client ID (UUID) to send in the request.
     * @return true if fetching and parsing were successful and websocket config is available,
     *         or if activation challenge is present, false otherwise.
     */
    bool CheckVersion(const std::string &ota_url, const std::string &device_id, const std::string &client_id);

    /**
     * @brief Attempts to activate the device using the challenge obtained from Fetch.
     *
     * @return ActivationStatus indicating the result of the activation attempt.
     */
    ActivationStatus Activate();

    /**
     * @brief Checks if an activation challenge was received during the last Fetch.
     *
     * @return true if an activation challenge is available, false otherwise.
     */
    bool HasActivationChallenge() const
    {
        return has_activation_challenge_;
    }

    /**
     * @brief Gets the activation message received from the server, if any.
     *
     * @return const std::string& The activation message.
     */
    const std::string &GetActivationMessage() const
    {
        return activation_message_;
    }

    // Getter methods for the fetched configuration
    const std::string &GetServerAddress() const
    {
        return serverAddress_;
    }
    int GetServerPort() const
    {
        return serverPort_;
    }
    const std::string &GetServerPath() const
    {
        return serverPath_;
    }
    const std::string &GetAccessToken() const
    {
        return accessToken_;
    }

private:
    // Callback function for curl write operation
    static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);

    // Helper to generate activation payload
    std::string GetActivationPayload();

    // Private constructor for singleton pattern
    AITalkOta();

private:
    // Member variables to store fetched configuration
    std::string serverAddress_;
    int serverPort_ = 0; // Initialize to indicate not set
    std::string serverPath_;
    std::string accessToken_;

    // Activation related members
    std::string ota_url_; // Base URL for OTA and activation
    std::string serial_number_;
    bool has_serial_number_ = false;
    std::string activation_message_;
    std::string activation_code_; // Store if needed, e.g., for display
    std::string activation_challenge_;
    bool has_activation_challenge_ = false;
    int activation_timeout_ms_ = 0;

    std::string device_id_;
    std::string client_id_;
};

} // namespace AITalk
} // namespace El
